package com.neusoft.neuiotms.dcam.common.fixture;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.neusoft.bizcore.cache.caching.CacheService;
import com.neusoft.neuiotms.common.converter.DictionaryConverter;
import com.neusoft.neuiotms.common.dto.DictionaryDTO;
import com.neusoft.neuiotms.common.model.Dictionary;
import com.neusoft.neuiotms.common.model.DictionaryType;
import com.neusoft.neuiotms.common.model.SerialNumber;
import com.neusoft.neuiotms.common.model.SysTask;
import com.neusoft.neuiotms.common.model.enums.SysTaskStatus;
import com.neusoft.neuiotms.common.repository.DictionaryRepository;
import com.neusoft.neuiotms.common.repository.DictionaryTypeRepository;
import com.neusoft.neuiotms.common.repository.SerialNumberRepository;
import com.neusoft.neuiotms.common.repository.SysTaskRepository;
import com.neusoft.neuiotms.dcam.constant.DCAMConsts;
import com.neusoft.neuiotms.dcam.fixed.model.AssetCodeRule;
import com.neusoft.neuiotms.dcam.fixed.model.AssetType;
import com.neusoft.neuiotms.dcam.fixed.repository.AssetCodeRuleRepository;
import com.neusoft.neuiotms.dcam.fixed.repository.AssetTypeRepository;
import com.univocity.parsers.annotations.NullString;
import com.univocity.parsers.annotations.Parsed;
import com.univocity.parsers.common.processor.BeanListProcessor;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@Order(value = 1)
public class DcamInitialDataFixture implements CommandLineRunner {

    @Autowired
    private SerialNumberRepository serialNumberRepository;
    @Autowired
    private DictionaryTypeRepository dictionaryTypeRepository;
    @Autowired
    private DictionaryRepository dictionaryRepository;
    @Autowired
    private AssetCodeRuleRepository assetCodeRuleRepository;
    @Autowired
    private SysTaskRepository SysTaskRepository;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private AssetTypeRepository assetTypeRepository;
    @Autowired
    private DictionaryConverter dictionaryConverter;
    @Autowired
    private CacheService cacheService;
    public static String CACHE_COMMON_DIC = "CACHE_COMMON_DIC";

    @Override
    public void run(final String... args) throws Exception {
        DcamInitialDataFixture.log.info("初始化数据开始");
        // 初始化字典表数据
        this.initDictionaryType();
        // 初始化流水号表
        this.initSerialNumber();
        // 初始化资产编码
        this.initAssetCodeRule();
        //初始化维保到期提醒任务
        this.initSysTask();
        //初始化资产类型
        this.initAssetType();
        // 初始化缓存
        this.initDicCache();

        DcamInitialDataFixture.log.info("初始化数据结束");
    }

    private void initDicCache() {
        final List<DictionaryType> types = this.dictionaryTypeRepository.findAll();
        final List<Dictionary> list = this.dictionaryRepository.findAll();

        final List<DictionaryDTO> cacheList = new ArrayList<>();
        types.forEach(m -> {
            cacheList.clear();

            list.stream().filter(s -> s.getDictionaryType().getType().equals(m.getType()))
                    .map(k -> this.dictionaryConverter.toDTO(k)).forEach(cacheList::add);

            if (!this.cacheService.isInCache(DcamInitialDataFixture.CACHE_COMMON_DIC, m.getType())) {
                this.cacheService.put(DcamInitialDataFixture.CACHE_COMMON_DIC, m.getType(), cacheList);
            }
        });

    }

    private void initDictionaryType() throws IOException {
        DcamInitialDataFixture.log.info("初始化字典表数据开始");
        if (this.dictionaryTypeRepository.findAll().size() > 0) {
            DcamInitialDataFixture.log.info("无需初始化字典表");
            return;
        }
        final CsvParserSettings settings = new CsvParserSettings();
        settings.getFormat().setLineSeparator("\n");
        // 可以将解析器配置成自动检测输入中的行分隔符
        settings.setLineSeparatorDetectionEnabled(true);
        settings.setHeaderExtractionEnabled(true);

        final BeanListProcessor<DictionaryTypeBean> processor = new BeanListProcessor<>(DictionaryTypeBean.class);
        settings.setProcessor(processor);

        final CsvParser parser = new CsvParser(settings);
        final org.springframework.core.io.Resource csvFile = this.applicationContext
                .getResource("classpath:fixture/neuiot_dictionary_type.csv");

        if (!csvFile.exists()) {
            return;
        }
        parser.parse(csvFile.getInputStream());
        final List<DictionaryTypeBean> typeBeans = processor.getBeans();
        final List<DictionaryBean> dictionarys = this.initDictionary();

        final List<Dictionary> saveList = new ArrayList<>();
        for (final DictionaryTypeBean typeBean : typeBeans) {
            final DictionaryType setting = new DictionaryType();
            BeanUtils.copyProperties(typeBean, setting);
            final DictionaryType model = this.dictionaryTypeRepository.save(setting);
            final List<DictionaryBean> everyTypedictionary = dictionarys.stream()
                    .filter(t -> t.getDictionary_type_id() == typeBean.getId()).collect(Collectors.toList());
            final List<Dictionary> dictionary = everyTypedictionary.stream().map(k -> {
                final Dictionary d = new Dictionary();
                BeanUtils.copyProperties(k, d);
                d.setDictionaryType(model);
                return d;
            }).collect(Collectors.toList());
            saveList.addAll(dictionary);
        }

        if (!saveList.isEmpty()) {
            this.dictionaryRepository.saveAll(saveList);
        }
        DcamInitialDataFixture.log.info("初始化字典表数据结束");
    }

    private List<DictionaryBean> initDictionary() throws IOException {
        final CsvParserSettings dictionarySettings = new CsvParserSettings();
        dictionarySettings.getFormat().setLineSeparator("\n");
        // 可以将解析器配置成自动检测输入中的行分隔符
        dictionarySettings.setLineSeparatorDetectionEnabled(true);
        dictionarySettings.setHeaderExtractionEnabled(true);

        final BeanListProcessor<DictionaryBean> dictionaryprocessor = new BeanListProcessor<>(DictionaryBean.class);
        dictionarySettings.setProcessor(dictionaryprocessor);

        final CsvParser parser1 = new CsvParser(dictionarySettings);
        final org.springframework.core.io.Resource csvDictionaryFile = this.applicationContext
                .getResource("classpath:fixture/neuiot_dictionary.csv");

        if (!csvDictionaryFile.exists()) {
            return new ArrayList<>();
        }

        parser1.parse(csvDictionaryFile.getInputStream());
        return dictionaryprocessor.getBeans();
    }

    private void initSerialNumber() throws IOException {
        DcamInitialDataFixture.log.info("初始化流水号表数据开始");
        if (this.serialNumberRepository.findAll().size() > 0) {
            DcamInitialDataFixture.log.info("无需初始化流水号表");
            return;
        }
        final CsvParserSettings settings = new CsvParserSettings();
        settings.getFormat().setLineSeparator("\n");
        // 可以将解析器配置成自动检测输入中的行分隔符
        settings.setLineSeparatorDetectionEnabled(true);
        settings.setHeaderExtractionEnabled(true);

        final BeanListProcessor<SerialNumberBean> processor = new BeanListProcessor<>(SerialNumberBean.class);
        settings.setProcessor(processor);

        final CsvParser parser = new CsvParser(settings);
        final org.springframework.core.io.Resource csvFile = this.applicationContext
                .getResource("classpath:fixture/neuiot_serial_number.csv");

        if (!csvFile.exists()) {
            return;
        }
        parser.parse(csvFile.getInputStream());
        final List<SerialNumberBean> typeBeans = processor.getBeans();

        final List<SerialNumber> serialNumber = typeBeans.stream().map(m -> {
            final SerialNumber number = new SerialNumber();
            BeanUtils.copyProperties(m, number);
            return number;
        }).collect(Collectors.toList());
        if (!serialNumber.isEmpty()) {
            this.serialNumberRepository.saveAll(serialNumber);
        }
        DcamInitialDataFixture.log.info("初始化流水号表数据结束");
    }

    private void initAssetType() throws IOException {
        DcamInitialDataFixture.log.info("初始化资产分类表数据开始");
        if (this.assetTypeRepository.findAll().size() > 0) {
            DcamInitialDataFixture.log.info("无需初始化资产分类表");
            return;
        }
        final CsvParserSettings settings = new CsvParserSettings();
        settings.getFormat().setLineSeparator("\n");
        // 可以将解析器配置成自动检测输入中的行分隔符
        settings.setLineSeparatorDetectionEnabled(true);
        settings.setHeaderExtractionEnabled(true);

        final BeanListProcessor<AssetTypeBean> processor = new BeanListProcessor<>(AssetTypeBean.class);
        settings.setProcessor(processor);

        final CsvParser parser = new CsvParser(settings);
        final org.springframework.core.io.Resource csvFile = this.applicationContext
                .getResource("classpath:fixture/neuiot_fixed_asset_type.csv");

        if (!csvFile.exists()) {
            return;
        }
        parser.parse(csvFile.getInputStream());
        final List<AssetTypeBean> typeBeans = processor.getBeans();

        final List<AssetTypeBean> topBeans =
                typeBeans.stream().filter(t -> t.getParentId() == null).collect(Collectors.toList());
        if (topBeans.isEmpty()) {
            return;
        }
        typeBeans.removeAll(topBeans);

        if (!topBeans.isEmpty()) {
            final List<AssetType> type = topBeans.stream().map(t -> {
                final AssetType a = new AssetType();
                BeanUtils.copyProperties(t, a);
                return a;
            }).collect(Collectors.toList());
            List<AssetType> topTypes = this.assetTypeRepository.saveAll(type);
            while (!typeBeans.isEmpty()) {
                topTypes = this.assetTypeRepository.saveAll(this.getChild(topTypes, topBeans, typeBeans));
            }
        }
        DcamInitialDataFixture.log.info("初始化资产分类表数据结束");
    }

    private List<AssetType> getChild(final List<AssetType> types, final List<AssetTypeBean> topBeans,
            final List<AssetTypeBean> typeBeans) {
        final List<AssetType> saveList = new ArrayList<>();
        final List<AssetTypeBean> beans = new ArrayList<>();
        for (int i = 0; i < types.size(); i++) {
            final AssetTypeBean typeBean = topBeans.get(i);
            final AssetType type = types.get(i);
            final List<AssetTypeBean> child =
                    typeBeans.stream().filter(b -> b.getParentId() == typeBean.getId()).collect(Collectors.toList());
            typeBeans.removeAll(child);
            beans.addAll(child);
            child.forEach(c -> {
                final AssetType childType = new AssetType();
                BeanUtils.copyProperties(c, childType);
                childType.setParent(type);
                saveList.add(childType);
            });
        }
        topBeans.clear();
        topBeans.addAll(beans);
        return saveList;
    }

    private void initAssetCodeRule() {
        DcamInitialDataFixture.log.info("初始化资产编码规则表数据开始");
        if (this.assetCodeRuleRepository.findAll().size() > 0) {
            DcamInitialDataFixture.log.info("无需初始化资产编码规则表");
            return;
        }
        final AssetCodeRule codeRule = new AssetCodeRule();
        final SerialNumber number = this.serialNumberRepository.findByModuleCode(DCAMConsts.ASSET_CODE_RULE);
        codeRule.setSerialNumber(number);
        final String[] fileds = {"Year6", "Day", "AssetTypeCode", "OrgId" };
        codeRule.setFileds(Arrays.asList(fileds));
        this.assetCodeRuleRepository.save(codeRule);
        DcamInitialDataFixture.log.info("初始化资产编码规则表数据结束");
    }

    private void initSysTask() {
        DcamInitialDataFixture.log.info("初始化定时任务数据开始");
        if (this.SysTaskRepository.findAll().size() > 0) {
            DcamInitialDataFixture.log.info("无需初始化定时任务");
            return;
        }
        final SysTask sysTask = new SysTask();
        sysTask.setCode("TASK_USJ4DNTDJYQH");
        sysTask.setCron("0 0 12 * * ?");
        sysTask.setJobClass("com.neusoft.neuiotms.dcam.task.MaintenanceReminderJob");
        sysTask.setJobGroup("maintenance");
        sysTask.setJobDesc("维保到期管理");
        sysTask.setName("维保到期管理");
        sysTask.setStatus(SysTaskStatus.active);
        this.SysTaskRepository.save(sysTask);
        DcamInitialDataFixture.log.info("初始化定时任务数据结束");
    }

    @Getter
    @Setter
    @ToString
    public static class DictionaryTypeBean {
        @Parsed(field = "id")
        private Long id;

        @Parsed(field = "text")
        private String text;

        @Parsed(field = "type")
        private String type;

    }

    @Getter
    @Setter
    @ToString
    public static class DictionaryBean {
        @Parsed(field = "id")
        private Long id;

        @Parsed(field = "code")
        private String code;

        @NullString(nulls = {"null" })
        @Parsed(field = "sort")
        private Integer sort;

        @Parsed(field = "text")
        private String text;

        @Parsed(field = "dictionary_type_id")
        private Long dictionary_type_id;

    }

    @Getter
    @Setter
    @ToString
    public static class SerialNumberBean {
        @Parsed(field = "id")
        private Long id;

        @Parsed(field = "auto_increment")
        private Byte autoIncrement;

        @Parsed(field = "config_templet")
        private String configTemplet;

        @Parsed(field = "max_serial")
        private Integer maxSerial;

        @Parsed(field = "module_code")
        private String moduleCode;

        @Parsed(field = "module_name")
        private String moduleName;

        @Parsed(field = "pre_max_num")
        private Integer preMaxNum;
    }

    @Getter
    @Setter
    @ToString
    public static class AssetTypeBean {
        @Parsed(field = "id")
        private Long id;

        @Parsed(field = "code")
        private String code;

        @Parsed(field = "name")
        private String name;

        @Parsed(field = "sort")
        private Integer sort;

        @Parsed(field = "parent_id")
        private Long parentId;

        @Parsed(field = "measure_unit")
        private String measureUnit;

    }

}
